////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNMemory/inc/WNMemory.h"
#include "WNCore/inc/WNAssert.h"
#include "WNMath/inc/WNMatrix44.h"
#include "WNMath/inc/WNVector3.h"
#include "WNMath/inc/WNVector4.h"
#include "WNMath/inc/WNPoint3.h"
#include "WNMath/inc/WNQuaternion.h"
#include "WNMath/inc/WNBasicMath.h"

namespace WNMath {
    WNMatrix44 operator * (WN_FLOAT32 _f, const WNMatrix44& _matrix) {
        return(WNMatrix44(_f * _matrix._11, _f * _matrix._12, _f * _matrix._13, _f * _matrix._14,
                          _f * _matrix._21, _f * _matrix._22, _f * _matrix._23, _f * _matrix._24,
                          _f * _matrix._31, _f * _matrix._32, _f * _matrix._33, _f * _matrix._34,
                          _f * _matrix._41, _f * _matrix._42, _f * _matrix._43, _f * _matrix._44));
    }

    WNMatrix44 operator / (WN_FLOAT32 _f, const WNMatrix44& _matrix) {
        const WN_FLOAT32 fInv = 1.0f / _f;

        return(WNMatrix44(fInv * _matrix._11, fInv * _matrix._12, fInv * _matrix._13, fInv * _matrix._14,
                          fInv * _matrix._21, fInv * _matrix._22, fInv * _matrix._23, fInv * _matrix._24,
                          fInv * _matrix._31, fInv * _matrix._32, fInv * _matrix._33, fInv * _matrix._34,
                          fInv * _matrix._41, fInv * _matrix._42, fInv * _matrix._43, fInv * _matrix._44));
    }
}

using namespace WNMath;

// Constructors ////////////////////////////////////////////////////////////////////////////////////

WNMatrix44::WNMatrix44() :
    _11(1.0f), _12(0.0f), _13(0.0f), _14(0.0f),
    _21(0.0f), _22(1.0f), _23(0.0f), _24(0.0f),
    _31(0.0f), _32(0.0f), _33(1.0f), _34(0.0f),
    _41(0.0f), _42(0.0f), _43(0.0f), _44(1.0f) {
}

WNMatrix44::WNMatrix44(const WN_FLOAT32* _f) :
        _11(_f[0]),  _12(_f[1]),  _13(_f[2]),  _14(_f[3]),
        _21(_f[4]),  _22(_f[5]),  _23(_f[6]),  _24(_f[7]),
        _31(_f[8]),  _32(_f[9]), _33(_f[10]), _34(_f[11]),
       _41(_f[12]), _42(_f[13]), _43(_f[14]), _44(_f[15]) {
}

WNMatrix44::WNMatrix44(const WNVector3& _vector1, const WNVector3& _vector2,
                       const WNVector3& _vector3, const WNVector3& _vector4) :
    _11(_vector1.x), _12(_vector2.x), _13(_vector3.x), _14(_vector4.x),
    _21(_vector1.y), _22(_vector2.y), _23(_vector3.y), _24(_vector4.y),
    _31(_vector1.z), _32(_vector2.z), _33(_vector3.z), _34(_vector4.z),
          _41(0.0f),       _42(0.0f),       _43(0.0f),       _44(1.0f) {
}

WNMatrix44::WNMatrix44(WN_FLOAT32 _m11, WN_FLOAT32 _m12, WN_FLOAT32 _m13, WN_FLOAT32 _m14,
                       WN_FLOAT32 _m21, WN_FLOAT32 _m22, WN_FLOAT32 _m23, WN_FLOAT32 _m24,
                       WN_FLOAT32 _m31, WN_FLOAT32 _m32, WN_FLOAT32 _m33, WN_FLOAT32 _m34,
                       WN_FLOAT32 _m41, WN_FLOAT32 _m42, WN_FLOAT32 _m43, WN_FLOAT32 _m44) :
    _11(_m11), _12(_m12), _13(_m13), _14(_m14),
    _21(_m21), _22(_m22), _23(_m23), _24(_m24),
    _31(_m31), _32(_m32), _33(_m33), _34(_m34),
    _41(_m41), _42(_m42), _43(_m43), _44(_m44) {
}

// Operators ///////////////////////////////////////////////////////////////////////////////////////

WN_FLOAT32& WNMatrix44::operator () (WN_INT32 _nRow, WN_INT32 _nCol) {
    WN_DEBUG_ASSERT(_nRow < 4 && _nCol < 4);

    return(*(&_11 + ((_nRow * 4) + _nCol)));
}

WN_FLOAT32 WNMatrix44::operator () (WN_INT32 _nRow, WN_INT32 _nCol) const {
    WN_DEBUG_ASSERT(_nRow < 4 && _nCol < 4);

    return(*(&_11 + ((_nRow * 4) + _nCol)));
}

WNMatrix44::operator WN_FLOAT32* () {
    return(&_11);
}

WNMatrix44::operator const WN_FLOAT32* () const {
    return(&_11);
}

WNMatrix44& WNMatrix44::operator *= (const WNMatrix44& _matrix) {
    const WNMatrix44 matrix = *this;

    _11 = matrix._11 * _matrix._11 + matrix._12 * _matrix._21 + matrix._13 * _matrix._31 + matrix._14 * _matrix._41;
    _12 = matrix._11 * _matrix._12 + matrix._12 * _matrix._22 + matrix._13 * _matrix._32 + matrix._14 * _matrix._42;
    _13 = matrix._11 * _matrix._13 + matrix._12 * _matrix._23 + matrix._13 * _matrix._33 + matrix._14 * _matrix._43;
    _14 = matrix._11 * _matrix._14 + matrix._12 * _matrix._24 + matrix._13 * _matrix._34 + matrix._14 * _matrix._44;
    _21 = matrix._21 * _matrix._11 + matrix._22 * _matrix._21 + matrix._23 * _matrix._31 + matrix._24 * _matrix._41;
    _22 = matrix._21 * _matrix._12 + matrix._22 * _matrix._22 + matrix._23 * _matrix._32 + matrix._24 * _matrix._42;
    _23 = matrix._21 * _matrix._13 + matrix._22 * _matrix._23 + matrix._23 * _matrix._33 + matrix._24 * _matrix._43;
    _24 = matrix._21 * _matrix._14 + matrix._22 * _matrix._24 + matrix._23 * _matrix._34 + matrix._24 * _matrix._44;
    _31 = matrix._31 * _matrix._11 + matrix._32 * _matrix._21 + matrix._33 * _matrix._31 + matrix._34 * _matrix._41;
    _32 = matrix._31 * _matrix._12 + matrix._32 * _matrix._22 + matrix._33 * _matrix._32 + matrix._34 * _matrix._42;
    _33 = matrix._31 * _matrix._13 + matrix._32 * _matrix._23 + matrix._33 * _matrix._33 + matrix._34 * _matrix._43;
    _34 = matrix._31 * _matrix._14 + matrix._32 * _matrix._24 + matrix._33 * _matrix._34 + matrix._34 * _matrix._44;
    _41 = matrix._41 * _matrix._11 + matrix._42 * _matrix._21 + matrix._43 * _matrix._31 + matrix._44 * _matrix._41;
    _42 = matrix._41 * _matrix._12 + matrix._42 * _matrix._22 + matrix._43 * _matrix._32 + matrix._44 * _matrix._42;
    _43 = matrix._41 * _matrix._13 + matrix._42 * _matrix._23 + matrix._43 * _matrix._33 + matrix._44 * _matrix._43;
    _44 = matrix._41 * _matrix._14 + matrix._42 * _matrix._24 + matrix._43 * _matrix._34 + matrix._44 * _matrix._44;

    return(*this);
}

WNMatrix44& WNMatrix44::operator += (const WNMatrix44& _matrix) {
    _11 += _matrix._11; _12 += _matrix._12; _13 += _matrix._13; _14 += _matrix._14;
    _21 += _matrix._21; _22 += _matrix._22; _23 += _matrix._23; _24 += _matrix._24;
    _31 += _matrix._31; _32 += _matrix._32; _33 += _matrix._33; _34 += _matrix._34;
    _41 += _matrix._41; _42 += _matrix._42; _43 += _matrix._43; _44 += _matrix._44;

    return(*this);
}

WNMatrix44& WNMatrix44::operator -= (const WNMatrix44& _matrix) {
    _11 -= _matrix._11; _12 -= _matrix._12; _13 -= _matrix._13; _14 -= _matrix._14;
    _21 -= _matrix._21; _22 -= _matrix._22; _23 -= _matrix._23; _24 -= _matrix._24;
    _31 -= _matrix._31; _32 -= _matrix._32; _33 -= _matrix._33; _34 -= _matrix._34;
    _41 -= _matrix._41; _42 -= _matrix._42; _43 -= _matrix._43; _44 -= _matrix._44;

    return(*this);
}

WNMatrix44& WNMatrix44::operator *= (WN_FLOAT32 _f) {
    _11 *= _f; _12 *= _f; _13 *= _f; _14 *= _f;
    _21 *= _f; _22 *= _f; _23 *= _f; _24 *= _f;
    _31 *= _f; _32 *= _f; _33 *= _f; _34 *= _f;
    _41 *= _f; _42 *= _f; _43 *= _f; _44 *= _f;

    return(*this);
}

WNMatrix44& WNMatrix44::operator /= (WN_FLOAT32 _f) {
    const WN_FLOAT32 fInv = 1.0f / _f;

    _11 *= fInv; _12 *= fInv; _13 *= fInv; _14 *= fInv;
    _21 *= fInv; _22 *= fInv; _23 *= fInv; _24 *= fInv;
    _31 *= fInv; _32 *= fInv; _33 *= fInv; _34 *= fInv;
    _41 *= fInv; _42 *= fInv; _43 *= fInv; _44 *= fInv;

    return(*this);
}

WNMatrix44 WNMatrix44::operator + () const {
    return(*this);
}

WNMatrix44 WNMatrix44::operator - () const {
    return(WNMatrix44(-_11, -_12, -_13, -_14,
                      -_21, -_22, -_23, -_24,
                      -_31, -_32, -_33, -_34,
                      -_41, -_42, -_43, -_44));
}

WNMatrix44 WNMatrix44::operator * (const WNMatrix44& _matrix) const {
    return(WNMatrix44(_11 * _matrix._11 + _12 * _matrix._21 + _13 * _matrix._31 + _14 * _matrix._41,
                      _11 * _matrix._12 + _12 * _matrix._22 + _13 * _matrix._32 + _14 * _matrix._42,
                      _11 * _matrix._13 + _12 * _matrix._23 + _13 * _matrix._33 + _14 * _matrix._43,
                      _11 * _matrix._14 + _12 * _matrix._24 + _13 * _matrix._34 + _14 * _matrix._44,
                      _21 * _matrix._11 + _22 * _matrix._21 + _23 * _matrix._31 + _24 * _matrix._41,
                      _21 * _matrix._12 + _22 * _matrix._22 + _23 * _matrix._32 + _24 * _matrix._42,
                      _21 * _matrix._13 + _22 * _matrix._23 + _23 * _matrix._33 + _24 * _matrix._43,
                      _21 * _matrix._14 + _22 * _matrix._24 + _23 * _matrix._34 + _24 * _matrix._44,
                      _31 * _matrix._11 + _32 * _matrix._21 + _33 * _matrix._31 + _34 * _matrix._41,
                      _31 * _matrix._12 + _32 * _matrix._22 + _33 * _matrix._32 + _34 * _matrix._42,
                      _31 * _matrix._13 + _32 * _matrix._23 + _33 * _matrix._33 + _34 * _matrix._43,
                      _31 * _matrix._14 + _32 * _matrix._24 + _33 * _matrix._34 + _34 * _matrix._44,
                      _41 * _matrix._11 + _42 * _matrix._21 + _43 * _matrix._31 + _44 * _matrix._41,
                      _41 * _matrix._12 + _42 * _matrix._22 + _43 * _matrix._32 + _44 * _matrix._42,
                      _41 * _matrix._13 + _42 * _matrix._23 + _43 * _matrix._33 + _44 * _matrix._43,
                      _41 * _matrix._14 + _42 * _matrix._24 + _43 * _matrix._34 + _44 * _matrix._44));
}

WNMatrix44 WNMatrix44::operator + (const WNMatrix44& _matrix) const {
    return(WNMatrix44(_11 + _matrix._11, _12 + _matrix._12, _13 + _matrix._13, _14 + _matrix._14,
                      _21 + _matrix._21, _22 + _matrix._22, _23 + _matrix._23, _24 + _matrix._24,
                      _31 + _matrix._31, _32 + _matrix._32, _33 + _matrix._33, _34 + _matrix._34,
                      _41 + _matrix._41, _42 + _matrix._42, _43 + _matrix._43, _44 + _matrix._44));
}

WNMatrix44 WNMatrix44::operator - (const WNMatrix44& _matrix) const {
    return(WNMatrix44(_11 - _matrix._11, _12 - _matrix._12, _13 - _matrix._13, _14 - _matrix._14,
                      _21 - _matrix._21, _22 - _matrix._22, _23 - _matrix._23, _24 - _matrix._24,
                      _31 - _matrix._31, _32 - _matrix._32, _33 - _matrix._33, _34 - _matrix._34,
                      _41 - _matrix._41, _42 - _matrix._42, _43 - _matrix._43, _44 - _matrix._44));
}

WNMatrix44 WNMatrix44::operator * (WN_FLOAT32 _f) const {
    return(WNMatrix44(_11 * _f, _12 * _f, _13 * _f, _14 * _f,
                      _21 * _f, _22 * _f, _23 * _f, _24 * _f,
                      _31 * _f, _32 * _f, _33 * _f, _34 * _f,
                      _41 * _f, _42 * _f, _43 * _f, _44 * _f));
}

WNMatrix44 WNMatrix44::operator / (WN_FLOAT32 _f) const {
    const WN_FLOAT32 fInv = 1.0f / _f;

    return(WNMatrix44(_11 * fInv, _12 * fInv, _13 * fInv, _14 * fInv,
                      _21 * fInv, _22 * fInv, _23 * fInv, _24 * fInv,
                      _31 * fInv, _32 * fInv, _33 * fInv, _34 * fInv,
                      _41 * fInv, _42 * fInv, _43 * fInv, _44 * fInv));
}

WN_BOOL WNMatrix44::operator == (const WNMatrix44& _matrix) const {
    return(0 == WNMemCmp(this, &_matrix, sizeof(WNMatrix44)));
}

WN_BOOL WNMatrix44::operator != (const WNMatrix44& _matrix) const {
    return(0 != WNMemCmp(this, &_matrix, sizeof(WNMatrix44)));
}

// Members /////////////////////////////////////////////////////////////////////////////////////////

WN_VOID WNMatrix44::Zero() {
    WNMemSet(&_11, 0, sizeof(WNMatrix44));
}

WN_VOID WNMatrix44::Identity() {
    _11 = 1.0f; _12 = 0.0f; _13 = 0.0f; _14 = 0.0f;
    _21 = 0.0f; _22 = 1.0f; _23 = 0.0f; _24 = 0.0f;
    _31 = 0.0f; _32 = 0.0f; _33 = 1.0f; _34 = 0.0f;
    _41 = 0.0f; _42 = 0.0f; _43 = 0.0f; _44 = 1.0f;
}

WN_FLOAT32 WNMatrix44::Determinant() const {
    const WNMatrix44 matrix = *this;

    return(matrix._24 * matrix._23 * matrix._32 * matrix._41 - matrix._13 * matrix._24 * matrix._32 * matrix._41 -
           matrix._14 * matrix._22 * matrix._33 * matrix._41 + matrix._12 * matrix._24 * matrix._33 * matrix._41 +
           matrix._13 * matrix._22 * matrix._34 * matrix._41 - matrix._12 * matrix._23 * matrix._34 * matrix._41 -
           matrix._14 * matrix._23 * matrix._31 * matrix._42 + matrix._13 * matrix._24 * matrix._31 * matrix._42 +
           matrix._14 * matrix._21 * matrix._33 * matrix._42 - matrix._11 * matrix._24 * matrix._33 * matrix._42 -
           matrix._13 * matrix._21 * matrix._34 * matrix._42 + matrix._11 * matrix._23 * matrix._34 * matrix._42 +
           matrix._14 * matrix._22 * matrix._31 * matrix._43 - matrix._12 * matrix._24 * matrix._31 * matrix._43 -
           matrix._14 * matrix._21 * matrix._32 * matrix._43 + matrix._11 * matrix._24 * matrix._32 * matrix._43 +
           matrix._22 * matrix._21 * matrix._34 * matrix._43 - matrix._11 * matrix._22 * matrix._34 * matrix._43 -
           matrix._13 * matrix._22 * matrix._31 * matrix._44 + matrix._12 * matrix._23 * matrix._31 * matrix._44 +
           matrix._13 * matrix._21 * matrix._32 * matrix._44 - matrix._11 * matrix._23 * matrix._32 * matrix._44 -
           matrix._12 * matrix._21 * matrix._33 * matrix._44 + matrix._11 * matrix._22 * matrix._33 * matrix._44);
}

WN_FLOAT32 WNMatrix44::Trace() const {
    const WNMatrix44 matrix = *this;

    return(matrix._11 + matrix._22 + matrix._33 + matrix._44);
}

WN_VOID WNMatrix44::Transpose() {
    const WNMatrix44 matrix = *this;

    _11 = matrix._11;
    _21 = matrix._12;
    _31 = matrix._13;
    _41 = matrix._14;
    _12 = matrix._21;
    _22 = matrix._22;
    _32 = matrix._23;
    _42 = matrix._24;
    _13 = matrix._31;
    _23 = matrix._32;
    _33 = matrix._33;
    _43 = matrix._34;
    _14 = matrix._41;
    _24 = matrix._42;
    _34 = matrix._43;
    _44 = matrix._44;
}

WN_BOOL WNMatrix44::Invert() {
    const WN_FLOAT32 determinant = Determinant();

    if (determinant == 0.0f) {
        return(WN_FALSE);
    }

    const WN_FLOAT32 invDeterminant = 1.0f / determinant;
    const WNMatrix44 adjoint = GetAdjugate();

    *this = adjoint * invDeterminant;

    return(WN_TRUE);
}

WN_BOOL WNMatrix44::IsIdentity() const {
    return(_11 == 1.0f && _12 == 0.0f && _13 == 0.0f && _14 == 0.0f &&
           _21 == 0.0f && _22 == 1.0f && _23 == 0.0f && _24 == 0.0f &&
           _31 == 0.0f && _32 == 0.0f && _33 == 1.0f && _34 == 0.0f &&
           _41 == 0.0f && _42 == 0.0f && _43 == 0.0f && _44 == 1.0f);
}

WN_BOOL WNMatrix44::IsSymmetric() const {
    if ((_12 - _21) != 0.0f) {
        return(WN_FALSE);
    }

    if ((_13 - _31) != 0.0f) {
        return(WN_FALSE);
    }

    if ((_14 - _41) != 0.0f) {
        return(WN_FALSE);
    }

    if ((_23 - _32) != 0.0f) {
        return(WN_FALSE);
    }

    if ((_24 - _42) != 0.0f) {
        return(WN_FALSE);
    }

    if ((_34 - _43) != 0.0f) {
        return(WN_FALSE);
    }

    return(WN_TRUE);
}

WN_BOOL WNMatrix44::IsDiagonal() const {
    if (_12 != 0.0f || _13 != 0.0f || _14 != 0.0f ||
        _21 != 0.0f || _23 != 0.0f || _24 != 0.0f ||
        _31 != 0.0f || _32 != 0.0f || _34 != 0.0f ||
        _41 != 0.0f || _42 != 0.0f || _43 != 0.0f) {
            return(WN_FALSE);
    }

    return(WN_TRUE);
}

WN_BOOL WNMatrix44::IsRotated() const {
    if (_12 == 0.0f && _13 == 0.0f &&
        _21 == 0.0f && _23 == 0.0f &&
        _32 == 0.0f && _32 == 0.0f) {
            return(WN_FALSE);
    }

    return(WN_TRUE);
}

WN_VOID WNMatrix44::Set(const WN_FLOAT32* _f) {
    WNMemCpy(&_11, _f, sizeof(WNMatrix44));
}

WN_VOID WNMatrix44::Set(WN_FLOAT32 _m11, WN_FLOAT32 _m12, WN_FLOAT32 _m13, WN_FLOAT32 _m14,
                        WN_FLOAT32 _m21, WN_FLOAT32 _m22, WN_FLOAT32 _m23, WN_FLOAT32 _m24,
                        WN_FLOAT32 _m31, WN_FLOAT32 _m32, WN_FLOAT32 _m33, WN_FLOAT32 _m34,
                        WN_FLOAT32 _m41, WN_FLOAT32 _m42, WN_FLOAT32 _m43, WN_FLOAT32 _m44) {
        _11 = _m11; _12 = _m12; _13 = _m13; _14 = _m14;
        _21 = _m21; _22 = _m22; _23 = _m23; _24 = _m24;
        _31 = _m31; _32 = _m32; _33 = _m33; _34 = _m34;
        _41 = _m41; _42 = _m42; _43 = _m43; _44 = _m44;
}

WN_VOID WNMatrix44::SetRow(WN_UINT8 _row, const WNVector4& _data) {
    WN_DEBUG_ASSERT(_row > 3);

    const WN_UINT8 offset = _row * 4;

    *(&_11 + offset) = _data.x;
    *(&_11 + offset + 1) = _data.y;
    *(&_11 + offset + 2) = _data.z;
    *(&_11 + offset + 3) = _data.w;
}

WN_VOID WNMatrix44::SetColumn(WN_UINT8 _column, const WNVector4& _data) {
    WN_DEBUG_ASSERT(_column > 3);

    *(&_11 + _column) = _data.x;
    *(&_11 + _column + 4) = _data.y;
    *(&_11 + _column + 8) = _data.z;
    *(&_11 + _column + 12) = _data.w;
}

WNMatrix44 WNMatrix44::GetTransposed() const {
    WNMatrix44 matrix = *this;

    return(matrix.Transpose(), matrix);
}

WNMatrix44 WNMatrix44::GetInverted() const {
    WNMatrix44 matrix = *this;

    return(matrix.Invert(), matrix);
}

WNMatrix44 WNMatrix44::GetAdjugate() const {
    WNMatrix44 matrix = *this;

    const WN_FLOAT32 matrix11 = matrix._22 * matrix._33 * matrix._44 - matrix._22 * matrix._34 * matrix._43 -
                                matrix._32 * matrix._23 * matrix._44 + matrix._32 * matrix._24 * matrix._43 +
                                matrix._42 * matrix._23 * matrix._34 - matrix._42 * matrix._24 * matrix._33;
    const WN_FLOAT32 matrix12 = -(matrix._12 * matrix._33 * matrix._44 + matrix._12 * matrix._34 * matrix._43 +
                                  matrix._32 * matrix._13 * matrix._44 - matrix._32 * matrix._14 * matrix._43 -
                                  matrix._42 * matrix._13 * matrix._34 + matrix._42 * matrix._14 * matrix._33);
    const WN_FLOAT32 matrix13 = matrix._12 * matrix._23 * matrix._44 - matrix._12 * matrix._24 * matrix._43 -
                                matrix._22 * matrix._13 * matrix._44 + matrix._22 * matrix._14 * matrix._43 +
                                matrix._42 * matrix._13 * matrix._24 - matrix._42 * matrix._14 * matrix._23;
    const WN_FLOAT32 matrix14 = -(matrix._12 * matrix._23 * matrix._34 + matrix._12 * matrix._24 * matrix._33 +
                                  matrix._22 * matrix._13 * matrix._34 - matrix._22 * matrix._14 * matrix._33 -
                                  matrix._32 * matrix._13 * matrix._24 + matrix._32 * matrix._14 * matrix._23);
    const WN_FLOAT32 matrix21 = -(matrix._21 * matrix._33 * matrix._44 + matrix._21 * matrix._34 * matrix._43 +
                                  matrix._31 * matrix._23 * matrix._44 - matrix._31 * matrix._24 * matrix._43 -
                                  matrix._41 * matrix._23 * matrix._34 + matrix._41 * matrix._24 * matrix._33);
    const WN_FLOAT32 matrix22 = matrix._11 * matrix._33 * matrix._44 - matrix._11 * matrix._34 * matrix._43 -
                                matrix._31 * matrix._13 * matrix._44 + matrix._31 * matrix._14 * matrix._43 +
                                matrix._41 * matrix._13 * matrix._34 - matrix._41 * matrix._14 * matrix._33;
    const WN_FLOAT32 matrix23 = -(matrix._11 * matrix._23 * matrix._44 + matrix._11 * matrix._24 * matrix._43 +
                                  matrix._21* matrix._13 * matrix._44 - matrix._21 * matrix._14 * matrix._43 -
                                  matrix._41 * matrix._13 * matrix._24 + matrix._41 * matrix._14 * matrix._23);
    const WN_FLOAT32 matrix24 = matrix._11 * matrix._23 * matrix._34 - matrix._11 * matrix._24 * matrix._33 -
                                matrix._21 * matrix._13 * matrix._34 + matrix._21 * matrix._14 * matrix._33 +
                                matrix._31 * matrix._13 * matrix._24 - matrix._31 * matrix._14 * matrix._23;
    const WN_FLOAT32 matrix31 = matrix._21 * matrix._32 * matrix._44 - matrix._21 * matrix._34 * matrix._42 -
                                matrix._31 * matrix._22 * matrix._44 + matrix._31 * matrix._24 * matrix._42 +
                                matrix._41 * matrix._22 * matrix._34 - matrix._41 * matrix._24 * matrix._32;
    const WN_FLOAT32 matrix32 = -(matrix._11 * matrix._32 * matrix._44 + matrix._11 * matrix._34 * matrix._42 +
                                  matrix._31 * matrix._12 * matrix._44 - matrix._31 * matrix._14 * matrix._42 -
                                  matrix._41 * matrix._12 * matrix._34 + matrix._41 * matrix._14 * matrix._32);
    const WN_FLOAT32 matrix33 = matrix._11 * matrix._22 * matrix._44 - matrix._11 * matrix._24 * matrix._42 -
                                matrix._21 * matrix._12 * matrix._44 + matrix._21 * matrix._14 * matrix._42 +
                                matrix._41 * matrix._12 * matrix._24 - matrix._41 * matrix._14 * matrix._22;
    const WN_FLOAT32 matrix34 = -(matrix._11 * matrix._22 * matrix._34 + matrix._11 * matrix._24 * matrix._32 +
                                  matrix._21 * matrix._12 * matrix._34 - matrix._21 * matrix._14 * matrix._32 -
                                  matrix._31 * matrix._12 * matrix._24 + matrix._31 * matrix._14 * matrix._22);
    const WN_FLOAT32 matrix41 = -(matrix._21 * matrix._32 * matrix._43 + matrix._21 * matrix._33 * matrix._42 +
                                  matrix._31 * matrix._22 * matrix._43 - matrix._31 * matrix._23 * matrix._42 -
                                  matrix._41 * matrix._22 * matrix._33 + matrix._41 * matrix._23 * matrix._32);
    const WN_FLOAT32 matrix42 = matrix._11 * matrix._32 * matrix._43 - matrix._11 * matrix._33 * matrix._42 -
                                matrix._31 * matrix._12 * matrix._43 + matrix._31 * matrix._13 * matrix._42 +
                                matrix._41 * matrix._12 * matrix._33 - matrix._41 * matrix._13 * matrix._32;
    const WN_FLOAT32 matrix43 = -(matrix._11 * matrix._22 * matrix._43 + matrix._11 * matrix._23 * matrix._42 +
                                  matrix._21 * matrix._12 * matrix._43 - matrix._21  * matrix._13 * matrix._42 -
                                  matrix._41 * matrix._12 * matrix._23 + matrix._41 * matrix._13 * matrix._22);
    const WN_FLOAT32 matrix44 = matrix._11 * matrix._22 * matrix._33 - matrix._11 * matrix._23 * matrix._32 -
                                matrix._21 * matrix._12 * matrix._33 + matrix._21 * matrix._13 * matrix._32 +
                                matrix._31 * matrix._12 * matrix._23 - matrix._31 * matrix._13 * matrix._22;

    matrix._11 = matrix11;
    matrix._12 = matrix12;
    matrix._13 = matrix13;
    matrix._14 = matrix14;
    matrix._21 = matrix21;
    matrix._22 = matrix22;
    matrix._23 = matrix23;
    matrix._24 = matrix24;
    matrix._31 = matrix31;
    matrix._32 = matrix32;
    matrix._33 = matrix33;
    matrix._34 = matrix34;
    matrix._41 = matrix41;
    matrix._42 = matrix42;
    matrix._43 = matrix43;
    matrix._44 = matrix44;

    return(matrix);
}

WNVector4 WNMatrix44::GetRow(WN_UINT8 _row) const {
    WN_DEBUG_ASSERT(_row > 3);

    const WN_UINT8 offset = _row * 4;

    return(WNVector4(*(&_11 + offset),
                     *(&_11 + offset + 1),
                     *(&_11 + offset + 2),
                     *(&_11 + offset + 3)));
}

WNVector4 WNMatrix44::GetColumn(WN_UINT8 _column) const {
    WN_DEBUG_ASSERT(_column > 4);

    return(WNVector4(*(&_11 + _column),
                     *(&_11 + _column + 4),
                     *(&_11 + _column + 8),
                     *(&_11 + _column + 12)));
}

WNPoint3 WNMatrix44::Transform(const WNPoint3& _point) const {
    WNPoint3 point;

    point.x = _11 * _point.x + _12 * _point.y + _13 * _point.z + _14;
    point.y = _21 * _point.x + _22 * _point.y + _23 * _point.z + _24;
    point.z = _31 * _point.x + _32 * _point.y + _33 * _point.z + _34;

    return(point);
}

WNVector3 WNMatrix44::Transform(const WNVector3& _vector) const {
    WNVector3 vector;

    vector.x = _11 * _vector.x + _12 * _vector.y + _13 * _vector.z;
    vector.y = _21 * _vector.x + _22 * _vector.y + _23 * _vector.z;
    vector.z = _31 * _vector.x + _32 * _vector.y + _33 * _vector.z;

    return(vector);
}

WNVector4 WNMatrix44::Transform(const WNVector4& _vector) const {
    WNVector4 vector;

    vector.x = _11 * _vector.x + _12 * _vector.y + _13 * _vector.z + _14 * _vector.w;
    vector.y = _21 * _vector.x + _22 * _vector.y + _23 * _vector.z + _24 * _vector.w;
    vector.z = _31 * _vector.x + _32 * _vector.y + _33 * _vector.z + _34 * _vector.w;

    return(vector);
}

WNQuaternion WNMatrix44::ToQuaternion() const {
    const WN_FLOAT32 trace = Trace();
    WN_FLOAT32 s;
    WN_FLOAT32 x;
    WN_FLOAT32 y;
    WN_FLOAT32 z;
    WN_FLOAT32 w;

    if (trace > 0.0f) {
        s = WNInvSqrt(trace) * 0.5f;
        x = ( _23 - _32 ) * s;
        y = ( _13 - _31 ) * s;
        z = ( _21 - _12 ) * s;
        w = 4.0f * s;
    } else {
        if (_11 > _22 && _11 > _33) {
            s  = WNInvSqrt(1.0f + _11 - _22 - _33) * 0.5f;
            x = 4.0f * s;
            y = (_12 + _21) * s;
            z = (_31 + _13) * s;
            w = (_23 - _32) * s;
        } else if (_22 > _33) {
            s  = WNInvSqrt(1.0f + _22 - _11 - _33) * 0.5f;
            x = (_12 + _21) * s;
            y = 4.0f * s;
            z = (_23 + _32) * s;
            w = (_31 - _13) * s;
        } else {
            s  = WNInvSqrt(1.0f + _33 - _11 - _22) * 0.5f;
            x = (_31 + _13) * s;
            y = (_23 + _32) * s;
            z = 4.0f * s;
            w = (_12 - _21) * s;
        }
    }

    return(WNQuaternion(x, y, z, w));
}
